#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <openssl/md5.h>

// 将二进制MD5结果转换为十六进制字符串（C++风格）
std::string md5ToHex(const unsigned char* md5Bytes) {
    std::stringstream ss;
    ss << std::hex << std::setfill('0'); // 以十六进制输出，不足补0
    for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
        ss << std::setw(2) << static_cast<int>(md5Bytes[i]); // 每个字节占2位
    }
    return ss.str();
}

// 计算字符串的MD5
std::string md5String(const std::string& input) {
    MD5_CTX ctx;
    MD5_Init(&ctx);                  // 初始化上下文
    MD5_Update(&ctx, input.c_str(), input.size()); // 传入字符串数据
    unsigned char md5Result[MD5_DIGEST_LENGTH];
    MD5_Final(md5Result, &ctx);      // 计算最终结果
    return md5ToHex(md5Result);      // 转换为十六进制并返回
}

// 计算文件的MD5（支持大文件，分片读取）
std::string md5File(const std::string& filePath) {
    std::ifstream file(filePath, std::ios::binary); // 二进制模式打开文件
    if (!file.is_open()) {
        throw std::runtime_error("无法打开文件: " + filePath);
    }

    MD5_CTX ctx;
    MD5_Init(&ctx);
    const int bufferSize = 4096; // 缓冲区大小（可根据需求调整）
    unsigned char buffer[bufferSize];

    // 循环读取文件内容并更新MD5
    while (file.read(reinterpret_cast<char*>(buffer), bufferSize)) {
        MD5_Update(&ctx, buffer, bufferSize);
    }
    // 处理最后一次未读满缓冲区的数据
    MD5_Update(&ctx, buffer, file.gcount());

    unsigned char md5Result[MD5_DIGEST_LENGTH];
    MD5_Final(md5Result, &ctx);
    return md5ToHex(md5Result);
}

int main() {
    // 示例1：计算字符串的MD5
    std::string str = "hello world";
    std::string strMd5 = md5String(str);
    std::cout << "字符串 \"" << str << "\" 的MD5: " << strMd5 << std::endl;

    // 示例2：计算文件的MD5（替换为实际文件路径）
    std::string filePath = "test.txt"; // 测试文件，可自行创建
    try {
        std::string fileMd5 = md5File(filePath);
        std::cout << "文件 " << filePath << " 的MD5: " << fileMd5 << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
    }

    return 0;
}